#include <iomanip>
#include <sstream>
#include "ListingGenerator.h"

ListingGenerator::ListingGenerator(ScannerInterface* sc) {
	scanner=sc;
	sc->registerObserver((CharacterObserver*)this);
	sc->registerObserver((TokenObserver*)this);
	
	currentLine = 1;
	endOfFile = false;
	tokenCount = errorCount =0;
	
	initListing( sc->getFilename() );
}

ListingGenerator::~ListingGenerator() {
	if(listingFile.is_open()) {
		listingFile.close();
	}
 
	scanner->removeObserver((CharacterObserver*)this);
	scanner->removeObserver((TokenObserver*)this);
}

void ListingGenerator::updateCharacter(char currentChar) {
	listingFile << currentChar;
	
	if( currentChar=='\n' ) {
		listingFile << std::setfill('0') << std::setw(4) << ++currentLine;
		listingFile << ": ";
	} 
}

void ListingGenerator::updateToken(Token token) {
	if(token.getType() <= Token::ERROR_SENTRY) {
		++errorCount;
		addError(token);
	} else {
		++tokenCount;
	}
	
	if(token == TK_EOF) {
		generateReport();
	}
}

void ListingGenerator::updateError(std::string errorMsg) {
	errors.push_back(errorMsg);
}

void ListingGenerator::addError(Token t) {
	std::stringstream errorMsg;
	errorMsg << "\nLine " << t.getLineNum() << ": ";
	
	switch( t.getType() ) {
	case TK_UNKNOWN:
		errorMsg << "Unknown Token";
		break;
	case TK_ERROR_NUM_CONST_TOO_LARGE:
		errorMsg << "Numerical constant is out of the range of representable values";
		break;
	case TK_ERROR_CHAR_CONST:
		errorMsg << "Illegal character constant length";
		break;
	case TK_ERROR_UNTERMINATED_COMMENT:
		errorMsg << "Missing closing comment marker";
		break;
	default: // not an error
		return;
	}
	
	errorMsg << " at or near > " << t.getValue() << " <";
	errors.push_back( errorMsg.str() );
}

void ListingGenerator::initListing(std::string filename) {
	std::string newFilename = filename + ".listing";
	listingFile.open(newFilename.c_str(), std::ios::out | std::ios::trunc);
	if(!listingFile.is_open()) {
		perror("Error creating listing file");
	}
	listingFile << "Source Listing ( " << filename << " )\n\n0001: ";
}

void ListingGenerator::generateReport() {
	/* listing file is already closed, this must have been called again
	 * because of multiple calls to Scanner::getToken() after the first eof
	 * token */
	if(!listingFile.is_open()) return; 
	
	std::stringstream reportOutput;
	
	reportOutput << currentLine << " lines read\n" << tokenCount << 
		" tokens extracted\n" << errors.size() << " errors detected\n\n";
	
	listingFile << "\n\n" << reportOutput.str();
	
#if LIST_GEN_CONSOLE_OUTPUT
	std::cout << reportOutput.str();
#endif
	
	for(std::size_t i=0; i<errors.size(); i++) {
		listingFile << errors[i] << "\n";
#if LIST_GEN_CONSOLE_OUTPUT
		std::cout << errors[i] << "\n";
#endif		
	}
	
	listingFile.close();
}
